<template>
  <div>
    <div id="toolbar" class="tools-bar">
      <div class="btn-group">
        <el-button title="刷新" @click="refresh()">
          <i class="el-icon-refresh"></i>
        </el-button>
        <el-button title="原始大小" @click="changeSize('actual')">
          <i class="el-icon-full-screen"></i>
        </el-button>
        <el-button title="适合屏幕" @click="changeSize('fit')">
          <i class="el-icon-aim"></i>
        </el-button>
        <el-button title="放大尺寸" @click="changeSize('zoomin')">
          <i class="el-icon-zoom-in"></i>
        </el-button>
        <el-button title="缩小尺寸" @click="changeSize('zoomout')">
          <i class="el-icon-zoom-out"></i>
        </el-button>
        <span>&nbsp;</span>
        <el-button :disabled="!currentClickItem" @click="deleteItem" title="删除元素">
          <i style="color:#F56C6C;" class="el-icon-delete"></i>
        </el-button>
        <el-button @click="saveData" title="保存" :loading="saveLoading">
          <svg-icon style="color:#67C23A;" icon-class="save" />
        </el-button>
      </div>
    </div>
    <div class="full-content">
      <div class="left-option">
        <div class="info">
          <div class="title">工序列表</div>
          <div>
            <ul>
              <li class="fold" v-for="(floder,index) in list" :key="index"><span
                  @click.stop="floder.itemshow = !floder.itemshow"><i class="el-icon-folder-remove"
                    v-show="floder.itemshow"></i><i class="el-icon-folder-add"
                    v-show="!floder.itemshow"></i>&nbsp;{{floder.label}}</span>
                <ul v-show="floder.itemshow">
                  <li v-for="(item,index) in floder.item" :key="index" class="leaf" :draggable="item.draggable"
                    :class="{'not-draggable':!item.draggable,'draggable':item.draggable}"
                    @dragend="dragEnd($event,item)"><i class="el-icon-folder-add"
                      style="visibility: hidden"></i>{{item.label}}
                  </li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
      </div>
      <div class="mid-option">
        <div id="mountNode"></div>
      </div>
      <div class="right-option">
        <div class="info" style="flex:1;">
          <template v-if="!showNodeInfo">
            <div class="title">流程信息</div>
            <div class="info-group">
              <div class="info-label">流程序号</div>
              <div class="info-text">{{routeDataObj.route_code}}</div>
            </div>
            <div class="info-group">
              <div class="info-label">流程名称</div>
              <div class="info-text">{{routeDataObj.route_name}}</div>
            </div>
            <div class="info-group">
              <div class="info-label">流程版本</div>
              <div class="info-text">{{routeDataObj.version}}</div>
            </div>
            <div class="info-group">
              <div class="info-label">状态</div>
              <div class="info-text">{{routeDataObj.enabled == '1'? '已启用' : '已禁用'}}</div>
            </div>
          </template>
          <template v-else>
            <div class="title">工序设置</div>
            <div class="info-group">
              <div class="info-label">工序名称</div>
              <div class="info-text">{{curNodeInfo.group_name}}</div>
            </div>
            <div class="info-group">
              <div class="info-label">MES管控工序</div>
              <div class="info-text">
                <el-tag :type="curNodeInfo.mes_flag == 'Y'?'priamry':'info'">{{curNodeInfo.mes_flag == 'Y'?'是':'否'}}
                </el-tag>
              </div>
            </div>
            <div class="info-group">
              <div class="info-label">投入工序</div>
              <div class="info-text">
                <el-switch v-model="curNodeInfo.first_group" :disabled="curNodeInfo.mes_flag != 'Y'"></el-switch>
              </div>
            </div>
            <div class="info-group">
              <div class="info-label">产出工序</div>
              <div class="info-text">
                <el-switch active-color="#67C23A" v-model="curNodeInfo.end_group"
                  :disabled="curNodeInfo.mes_flag != 'Y'"></el-switch>
              </div>
            </div>
            <div class="info-group">
              <div class="info-label">维修工序</div>
              <div class="info-text">
                <el-switch active-color="#F56C6C" v-model="curNodeInfo.state_flag"
                  :disabled="curNodeInfo.mes_flag != 'Y'"></el-switch>
              </div>
            </div>
            <div class="info-group">
              <div class="info-label">统计良率工序</div>
              <div class="info-text">
                <el-switch active-color="#E6A23C" v-model="curNodeInfo.group_type"
                  :disabled="curNodeInfo.mes_flag != 'Y'"></el-switch>
              </div>
            </div>
          </template>
        </div>
        <div class="info">
          <div class="title">缩略图</div>
          <div ref="minimap" id="minimap"></div>
        </div>
      </div>
    </div>

  </div>
</template>

<script>
import _ from 'lodash'
import G6 from '@antv/g6'

let data = {}
let graph = {}
let curEdge = null //当前要添加的线
let curAnchorPoint = null //当前鼠标悬停的连接点

const ANCHOR_POINTS = [
  [0.5, 0],
  [1, 0.5],
  [0, 0.5],
  [0.5, 1],
]

function fittingString(str, maxWidth, fontSize) {
  let currentWidth = 0
  let res = str
  const pattern = new RegExp('[\u4E00-\u9FA5]+') // distinguish the Chinese charactors and letters
  str.split('').forEach((letter, i) => {
    if (currentWidth > maxWidth) return
    if (pattern.test(letter)) {
      // Chinese charactors
      currentWidth += fontSize
    } else {
      // get the width of single letter according to the fontSize
      currentWidth += G6.Util.getLetterWidth(letter, fontSize)
    }
    if (currentWidth > maxWidth) {
      res = `${str.substr(0, i)}\n${str.substr(i)}`
    }
  })
  return res
}

function addAnchorPoints(item, pointsList) {
  const group = item.getContainer()
  const { width, height } = item.getKeyShape().getBBox()
  pointsList.forEach((point, index) => {
    const [x, y] = point
    group.addShape('circle', {
      name: 'anchorPoint',
      attrs: {
        x: width * (x - 0.5),
        y: height * (y - 0.5),
        r: 4,
        lineWidth: 2,
        fill: '#FFFFFF',
        stroke: '#5AAAFF',
        cursor: 'crosshair',
      },
      isAnchorPoint: true,
      anchorPointIndex: index,
    })
  })
}

function removeAnchorPoints(item) {
  const group = item.getContainer()
  const lst = group.findAllByName('anchorPoint')
  lst.forEach((anchorPoint) => {
    group.removeChild(anchorPoint)
  })
}

function isEnabledAnchorPoint(ev) {
  return !!ev.target.get('isAnchorPoint')
}

G6.registerNode(
  'common',
  {
    options: {
      size: [100, 55],
      anchorPoints: ANCHOR_POINTS,
      style: {
        stroke: '#999999',
        fill: '#FFFFFF',
        fillOpacity: 0.1,
        radius: 5,
        cursor: 'move',
      },
    },
  },
  'rect',
)

G6.registerNode(
  'notmes',
  {
    options: {
      size: [100, 55],
      anchorPoints: ANCHOR_POINTS,
      style: {
        stroke: '#555555',
        fill: '#555555',
        fillOpacity: 0.1,
        radius: 5,
        cursor: 'move',
      },
    },
  },
  'rect',
)

G6.registerNode(
  'startend',
  {
    options: {
      size: [100, 45],
      anchorPoints: ANCHOR_POINTS,
      style: {
        stroke: '#66B1FF',
        fill: '#66B1FF',
        fillOpacity: 0.5,
        radius: 25,
        cursor: 'move',
      },
    },
  },
  'rect',
)

G6.registerBehavior('drag-add-edge', {
  getEvents() {
    return {
      'node:mouseenter': 'handleNodeMouseEnter',
      'node:mouseleave': 'handleNodeMouseLeave',
      'node:mousedown': 'handleNodeMouseDown',
      mousemove: 'handleMouseMove',
      mouseup: 'handleMouseUp',
    }
  },
  handleNodeMouseEnter(ev) {
    let item = ev.item
    addAnchorPoints(item, ANCHOR_POINTS)
  },
  handleNodeMouseLeave(ev) {
    let item = ev.item
    removeAnchorPoints(item)
  },
  handleNodeMouseDown(ev) {
    if (!isEnabledAnchorPoint(ev)) return
    const sourceNode = ev.item
    const sourceNodeId = sourceNode.getModel().id
    const sourceAnchorPointIndex = ev.target.get('anchorPointIndex')
    const sourceAnchorPoint = sourceNode.getAnchorPoints()[sourceAnchorPointIndex]
    const model = {
      type: 'line',
      style: {
        stroke: '#69C0FF',
        lineWidth: 2,
        lineAppendWidth: 3,
        endArrow: { path: 'M 0,0 L 4,3 L 4,-3 Z' },
        lineDash: [5, 5],
      },
      source: sourceNodeId,
      sourceAnchor: sourceAnchorPointIndex,
      target: {
        x: ev.x,
        y: ev.y,
      },
    }
    curEdge = graph.addItem('edge', model)
  },
  handleMouseMove(ev) {
    //悬停连接点的处理
    if (isEnabledAnchorPoint(ev)) {
      if (ev.target == curAnchorPoint) return
      else {
        if (curAnchorPoint) curAnchorPoint.attr('fill', '#FFFFFF')
        curAnchorPoint = ev.target
        curAnchorPoint.attr('fill', '#5AAAFF')
      }
    } else {
      if (curAnchorPoint) {
        curAnchorPoint.attr('fill', '#FFFFFF')
        curAnchorPoint = null
      }
    }
    //是否需要处理连线
    if (!curEdge) return
    if (isEnabledAnchorPoint(ev)) {
      const targetNode = ev.item
      const targetNodeId = targetNode.getModel().id
      const targetAnchor = ev.target.get('anchorPointIndex')
      graph.updateItem(curEdge, {
        target: targetNodeId,
        targetAnchor: targetAnchor,
      })
    } else {
      graph.updateItem(curEdge, {
        target: {
          x: ev.x,
          y: ev.y,
        },
        targetAnchor: undefined,
      })
    }
  },
  handleMouseUp(ev) {
    if (!curEdge) return
    const targetid = curEdge.getModel().target
    const sourceid = curEdge.getModel().source
    if (!_.isObject(targetid) && targetid != sourceid) {
      //判断是否已经有连线
      let exist = graph
        .getEdges()
        .find((i) => i.getModel().target == targetid && i.getModel().source == sourceid && i != curEdge)
      if (!!exist) {
        graph.removeItem(curEdge)
        curEdge = undefined
        return
      }
      graph.updateItem(curEdge, {
        id: `${sourceid}~${targetid}`,
        type: 'polyline',
        style: {
          radius: 8,
          stroke: '#AAAAAA',
          lineWidth: 2,
          lineAppendWidth: 3,
          endArrow: { path: 'M 0,0 L 4,3 L 4,-3 Z' },
          lineDash: false,
          cursor: 'pointer',
        },
      })
      curEdge = undefined
    } else {
      graph.removeItem(curEdge)
      curEdge = undefined
    }
  },
})

export default {
  name: 'index',
  components: {},
  data() {
    return {
      routeId: null,
      routeDataObj: {},
      list: [],
      nodeIndex: 1,
      defaultNodes: [
        //默认的开始结束两个节点
        {
          type: 'startend',
          x: 100,
          y: 100,
          sid: '0',
          label: '开始',
        },
        {
          type: 'startend',
          x: 500,
          y: 400,
          sid: '1',
          label: '结束',
        },
      ],
      currentClickItem: null,
      saveLoading: false,
      showNodeInfo: false,
      curNodeInfo: {},
    }
  },
  methods: {
    refresh() {
      graph.destroy()
      this.$refs.minimap.innerHTML = ''
      this.$request(this.$api.design + 'getSingelRoute', { id: this.routeId }).then((r) => {
        this.routeDataObj = r
        //渲染画布
        data = this.routeDataObj.info ? this.routeDataObj.info : {}
        this.initCavans()
        //获取流程节点
        this.$request(this.$api.design + 'getGroupList', { id: this.routeId }).then((r) => {
          this.list = r
        })
      })
    },
    initCavans() {
      const mountNode = document.getElementById('mountNode')
      const minimap = new G6.Minimap({
        size: [200, 160],
        className: 'minimap',
        type: 'delegate',
        container: 'minimap',
      })
      graph = new G6.Graph({
        container: 'mountNode', // String | HTMLElement，必须，在 Step 1 中创建的容器 id 或容器本身
        width: mountNode.offsetWidth, // Number，必须，图的宽度
        height: mountNode.offsetHeight, // Number，必须，图的高度
        layout: {
          width: 1440,
          height: 900,
        },
        modes: {
          default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'hover-node', 'add-edge', 'drag-add-edge'],
        },
        nodeStateStyles: {
          default: {
            lineWidth: 2,
          },
          hover: {
            fillOpacity: 0.5,
          },
          click: {
            lineWidth: 3,
            fillOpacity: 0.5,
          },
        },
        edgeStateStyles: {
          default: {
            lineWidth: 2,
            stroke: '#AAAAAA',
          },
          click: {
            lineWidth: 3,
            stroke: '#69C0FF',
          },
        },
        plugins: [minimap],
      })
      graph.data(data)
      graph.render() // 渲染
      if (!data.nodes || data.nodes.length == 0) {
        //添加开始结束
        this.addDefaultNode()
      }
      graph.on('node:mouseenter', (e) => {
        const nodeItem = e.item
        graph.setItemState(nodeItem, 'hover', true) // 设置当前节点的 hover 状态为 true
      })
      graph.on('node:mouseleave', (e) => {
        const nodeItem = e.item // 获取鼠标离开的节点元素对象
        graph.setItemState(nodeItem, 'hover', false) // 设置当前节点的 hover 状态为 false
      })
      graph.on('click', (e) => {
        const clickNodes = graph.findAllByState('node', 'click').concat(graph.findAllByState('edge', 'click'))
        clickNodes.forEach((cn) => {
          cn.clearStates('click')
          cn.setState('default', true)
        })
        const nodeItem = e.item // 获取被点击的节点元素对象
        if (nodeItem) {
          nodeItem.setState('click', true)
          this.currentClickItem = nodeItem
        } else {
          this.currentClickItem = null
        }
      })
    },
    dragEnd(e, item) {
      e = window.event || e
      if (!item.draggable) {
        return false
      }
      let newNode = {}
      let i = this.nodeIndex++
      Object.assign(newNode, item)
      const xy = graph.getPointByClient(e.x, e.y)
      newNode.label = fittingString(newNode.label, 80, 15)
      newNode.x = xy.x
      newNode.y = xy.y
      newNode.id = newNode.sid + '_' + i
      newNode.type = newNode.mes_flag == 'Y' ? 'common' : 'notmes'
      graph.addItem('node', newNode)
      item.draggable = false
    },
    changeSize(v) {
      if (v == 'fit') graph.fitView(30)
      else if (v == 'actual') graph.zoomTo(1)
      else if (v == 'zoomin') graph.zoom(1.2)
      else if (v == 'zoomout') graph.zoom(0.8)
    },
    addDefaultNode() {
      this.defaultNodes.forEach((item) => {
        let tmp = {}
        let i = this.nodeIndex++
        Object.assign(tmp, item)
        tmp.id = tmp.sid + '_' + i
        const node = graph.addItem('node', tmp)
      })
    },
    deleteItem() {
      if (!this.currentClickItem) {
        return
      }
      let sid = this.currentClickItem.getModel().sid
      if (sid) {
        let a = -1,
          b = -1
        a = _.findIndex(this.list, (i) => {
          b = _.findIndex(i.item, (j) => j.sid == sid)
          return b != -1
        })
        if (a != -1) this.list[a].item[b].draggable = true
      }
      graph.remove(this.currentClickItem)
      //工序设置的数据也要删除一下
      var i = this.routeDataObj.groups.findIndex((i) => i.group_next == sid)
      if (i != -1) this.routeDataObj.groups.splice(i, 1)
      this.currentClickItem = null
    },
    saveData() {
      let postData = _.cloneDeep(graph.save())
      let tmp = []
      postData.edges.forEach(function (v, index) {
        let groupName = v.source.split('_')
        let groupNext = v.target.split('_')
        tmp.push({ group_name: groupName[0], group_next: groupNext[0], step_sequence: index + 1 })
      })
      this.saveLoading = true
      this.$request(
        this.$api.design + 'saveRouteInfo',
        {
          id: this.routeId,
          data: tmp,
          info: JSON.stringify(postData),
          groupType: this.routeDataObj.groups,
        },
        'post',
      )
        .then((r) => {
          this.$message({
            message: '保存成功',
            type: 'success',
          })
          this.saveLoading = false
          this.refresh()
        })
        .catch(() => {
          this.saveLoading = false
        })
    },
  },
  created() {
    if (this.$route.params && this.$route.params.id) {
      this.routeId = this.$route.params.id
    } else {
      this.$message({
        message: '流程ID不能为空',
        type: 'error',
      })
    }
  },
  mounted() {
    if (this.routeId) {
      this.$request(this.$api.design + 'getSingelRoute', { id: this.routeId }).then((r) => {
        this.routeDataObj = r
        //渲染画布
        data = this.routeDataObj.info ? this.routeDataObj.info : {}
        this.initCavans()
        //获取流程节点
        this.$request(this.$api.design + 'getGroupList', { id: this.routeId }).then((r) => {
          this.list = r
        })
      })
    }
    document.onkeydown = (e) => {
      if (window.event.keyCode == 46 && this.currentClickItem) {
        this.deleteItem()
      }
    }
  },
  watch: {
    currentClickItem(v) {
      this.showNodeInfo = false
      if (v && v.get('type') == 'node') {
        let model = v.get('model')
        if (model.sid == '0' || model.sid == '1') return
        let t = this.routeDataObj.groups.find((i) => i.group_next == model.sid)
        if (!t)
          this.routeDataObj.groups.push({
            group_next: model.sid,
            group_name: model.label,
            group_type: false,
            state_flag: false,
            mes_flag: model.mes_flag,
            first_group: false,
            end_group: false,
          })
        this.curNodeInfo = this.routeDataObj.groups.find((i) => i.group_next == model.sid)
        this.showNodeInfo = true
      }
    },
    curNodeInfo: {
      handler(n, o) {
        if (n && o && n.group_next == o.group_next) {
          let flags = [0, 0, 0, 0] //投入/产出/维修/良率
          // 投入工序设置检查
          if (n.first_group == true) {
            flags[0] = 1
            let check = this.routeDataObj.groups.findIndex((i) => i.first_group && i.group_next != n.group_next)
            if (check != -1) {
              this.$message({
                message: '一个工艺流程不能设置多个投入站点',
                type: 'error',
              })
              n.first_group = false
              return false
            }
          }
          // 产出工序设置检查
          if (n.end_group == true) {
            flags[1] = 1
            let check = this.routeDataObj.groups.findIndex((i) => i.end_group && i.group_next != n.group_next)
            if (check != -1) {
              this.$message({
                message: '一个工艺流程不能设置多个产出站点',
                type: 'error',
              })
              n.end_group = false
              return false
            }
          }
          if (n.state_flag == true) {
            flags[2] = 1
          }
          if (n.group_type == true) {
            flags[3] = 1
          }
          // 改变NODE样式
          let nodes = graph.getNodes()
          let node = nodes.find((i) => i.get('model').sid == n.group_next)
          if (!node) return true
          let totalFlagCnt = _.sum(flags)
          let color = ''
          //默认色彩
          if (totalFlagCnt == 0) {
            graph.updateItem(node, { style: { fill: '#FFFFFF', stroke: '#999999' } })
            return
          }
          //紫色 多种状态
          else if (totalFlagCnt > 1) color = '#BE92ED'
          else color = ['#2979ff', '#19be6b', '#fa3534', '#ff9900'][flags.findIndex((item) => item == 1)]
          graph.updateItem(node, { style: { fill: color, stroke: color } })
        } else {
          return true
        }
      },
      immediate: true, //刷新加载 立马触发一次handler
      deep: true, // 可以深度检测到 person 对象的属性值的变化
    },
  },
}
</script>

<style lang="scss" scoped>
.full-content {
  height: calc(100vh - 136px);
  width: 100%;
  display: flex;
}
.tools-bar {
  background: #ffffff;
  padding: 0;
  border-bottom: 1px solid #e4e4e4;
}
::v-deep.btn-group .el-button {
  border: none;
  margin: 0;
  font-size: 16px;
  padding: 12px;
}
.left-option {
  width: 230px;
  height: 100%;
  border-right: 1px solid #e4e4e4;
  overflow-y: scroll;
}
.mid-option {
  flex: 1;
  height: 100%;
  overflow: hidden;
}
#mountNode {
  width: 100%;
  height: 100%;
}
.right-option {
  width: 200px;
  height: 100%;
  border-left: 1px solid #e4e4e4;
  display: flex;
  flex-direction: column;
}
.info .title {
  font-size: 14px;
  height: 34px;
  padding-left: 10px;
  border-top: 1px solid #e4e4e4;
  border-bottom: 1px solid #e4e4e4;
  background: #f4f4f4;
  line-height: 34px;
}
.info ul {
  padding: 5px 0;
  margin: 0;
}
.info li {
  font-size: 14px;
  line-height: 26px;
  padding-left: 8px;
  border: 1px solid transparent;
  border-radius: 5px;
  list-style-type: none;
}
.info .fold {
  cursor: pointer;
  font-weight: 700;
}
.info .leaf {
  cursor: pointer;
  font-weight: 500;
}
.info li.not-draggable {
  color: #9a9999;
}
.info li.draggable:hover {
  background: white;
  border: 1px solid #ced4d9;
  cursor: move;
}
.info-group {
  padding: 5px 10px;
  font-size: 14px;
  display: flex;
  margin-top: 5px;
}
.info-group .info-label {
  font-weight: 700;
}
.info-group .info-text {
  flex: 1;
  margin-left: 10px;
  word-break: break-all;
  text-align: right;
}
</style>

